#include "canvas.hh"
#include "shader.hh"

#include <utility>
#include <glm/vec2.hpp>
#include <glm/vec4.hpp>

#define GL_GLEXT_PROTOTYPES
#include <GL/glcorearb.h>


static GLuint program_dot;
static GLuint dot_pixsize;


//------ shader and program
namespace detail
{
	static void attach_shader(GLuint) {}

	template <class ...Ts>
	static void attach_shader(GLuint p, GLuint s, Ts... ss)
	{
		glAttachShader(p, s);
		attach_shader(p, ss...);
	}
}

static auto create_shader(GLenum type, char const* source)
{
	auto s = glCreateShader(type);
	glShaderSource(s, 1, &source, {});
	glCompileShader(s);
	return s;
}


template <class ...Ts>
static auto create_program(GLuint s, Ts... shaders)
{
	auto p = glCreateProgram();
	detail::attach_shader(p, s, shaders...);
	glLinkProgram(p);
	return p;
}


//------ data
struct point
{
	glm::vec2 pos;
	    float radius;
	glm::vec4 color;
};

static point points[] = {
	{{  0.0,  0.0 },  5, { 1, 0, 0, 1 }},
	{{ -0.5, -0.5 }, 10, { 0, 1, 0, 1 }},
	{{ -0.5, +0.5 }, 15, { 0, 0, 1, 1 }},
	{{ +0.5, +0.5 }, 20, { 1, 0, 1, 1 }},
	{{ +0.5, -0.5 }, 25, { 0, 1, 1, 1 }},
	{{ -0.3, -0.3 }, 30, { 0, 0, 1, 1 }},
	{{ -0.3, +0.3 }, 35, { 1, 0, 0, 1 }},
	{{ +0.3, +0.3 }, 60, { 0, 1, 0,0.5}},
	{{ +0.3, -0.3 }, 90, { 1, 0, 1,0.7}},
};


//------ create vao, vbo and programs
void canvas::init()
{
	{
		GLuint id;
		glGenVertexArrays(1, &id);
		glBindVertexArray(id);
	}

	{
		GLuint id;
		glGenBuffers(1, &id);
		glBindBuffer(GL_ARRAY_BUFFER, id);
		glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
		glEnableVertexAttribArray(0);
		glEnableVertexAttribArray(1);
		glEnableVertexAttribArray(2);
#define OFFSET(TYPE, MEMBER)	(&reinterpret_cast<TYPE*>(0)->MEMBER)
		glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(point), OFFSET(point, pos   ));
		glVertexAttribPointer(1, 1, GL_FLOAT, false, sizeof(point), OFFSET(point, radius));
		glVertexAttribPointer(2, 4, GL_FLOAT, false, sizeof(point), OFFSET(point, color ));
#undef OFFSET
	}


	{
		auto vs = create_shader(GL_VERTEX_SHADER  , shader::dot_vs);
		auto gs = create_shader(GL_GEOMETRY_SHADER, shader::dot_gs);
		auto fs = create_shader(GL_FRAGMENT_SHADER, shader::dot_fs);
		program_dot = create_program(vs, gs, fs);
		dot_pixsize = glGetUniformLocation(program_dot, "upixsize");
	}

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glClearColor(1, 1, 1, 1);
}


//------ render the scene and bloom effect
void canvas::render(float /*time*/)
{
	glUseProgram(program_dot);
	glClear(GL_COLOR_BUFFER_BIT);
	glDrawArrays(GL_POINTS, 0, sizeof(points)/sizeof(*points));
}


//------ reset the viewport and resize all textures
void canvas::resize(int w, int h)
{
	glViewport(0, 0, w, h);
	glUseProgram(program_dot);
	glUniform2f(dot_pixsize, 1.0 / w, 1.0 / h);
}

